package com.aruba.librouter_complier

import com.aruba.librouter_annotation.Router
import com.aruba.librouter_annotation.model.RouterMeta
import com.google.auto.service.AutoService
import java.lang.RuntimeException
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import javax.lang.model.type.TypeMirror
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
import javax.tools.Diagnostic
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy


/**
 * 注解解析器，每个模块都会执行一次该类中的方法
 * Created by aruba on 2021/11/22.
 */
@AutoService(Processor::class)
//指定要处理的注解
@SupportedAnnotationTypes("com.aruba.librouter_annotation.Router")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
class AnnotationProcessor : AbstractProcessor() {
    // 日志打印
    private lateinit var MSG: Messager
    private fun Messager.print(msg: String) {
        printMessage(Diagnostic.Kind.NOTE, msg);
    }

    // 节点工具类 (类、函数、属性都是节点) 用来获取节点
    private lateinit var mElementUtils: Elements

    // type(类信息)工具类   用来比对节点
    private lateinit var mTypeUtils: Types

    // 文件操作类  用来生成kotlin文件
    private lateinit var mFiler: Filer

    // Activity类的节点描述
    private val typeActivity by lazy { mElementUtils.getTypeElement(Const.ACTIVITY).asType() }

    // IService接口的节点描述
    private val typeIService by lazy { mElementUtils.getTypeElement(Const.ISERVICE).asType() }

    // 组名：RouterMeta列表的Map
    private val routerMetaByGroup: MutableMap<String, MutableList<RouterMeta>>
            by lazy { mutableMapOf() }

    // 组名：文件名列表的Map
    private val fileNameByGroup: MutableMap<String, String>
            by lazy { mutableMapOf() }

    private var isInit: Boolean = false

    override fun init(processingEnv: ProcessingEnvironment?) {
        super.init(processingEnv)
        //初始化
        processingEnv?.apply {
            MSG = messager
            MSG.print("init")
            mElementUtils = elementUtils
            mTypeUtils = typeUtils
            mFiler = filer
        }
    }

    /**
     * 开始执行
     */
    override fun process(
        typeElementSet: MutableSet<out TypeElement>?,
        roundEnvironment: RoundEnvironment?
    ): Boolean {
        if (isInit) return true
        isInit = true

        typeElementSet?.let {
            // 1.获取所有使用了Router注解的节点并遍历
            roundEnvironment?.getElementsAnnotatedWith(Router::class.java)?.forEach { element ->
                // 2.获取注解
                val router = element.getAnnotation(Router::class.java).apply {
                    if (path.isEmpty() || group.isEmpty()) {
                        MSG.print("$element group or path can not be empty")
                        throw RuntimeException()
                    }
                }
                // 3.获取包装类
                getRouterMeta(element, router, mTypeUtils) {
                    MSG.print(it.toString())
                }.apply {
                    MSG.print("group:${group} path:${path}")
                    // 4.加入缓存，即根据group分组
                    routerMetaByGroup.getOrPut(group) {
                        mutableListOf()
                    }.add(this)
                }
            }

            MSG.print(routerMetaByGroup.toString())
            // 5.利用kotlinpoet生成代码
            generateClassByKotlinPoet()
        }
        return true
    }

    /**
     * 获取节点包装类方法
     * 我们定义Router注解只能使用在Activity类和实现了IService接口的类
     */
    private fun getRouterMeta(
        element: Element,
        router: Router,
        typeUtils: Types,
        mirror: (mirror: TypeMirror) -> Unit = {}
    ): RouterMeta {
        // 获取当前节点的描述
        val type = element.asType().apply {
            mirror(this)
        }

        // 根据描述是否是typeActivity或者typeIService生成包装类
        return when {
            typeUtils.isSubtype(type, typeActivity) -> {//是Activity子类
                RouterMeta(path = router.path, group = router.group).apply {
                    this.element = element
                }
            }
            typeUtils.isSubtype(type, typeIService) -> {//实现了IService接口
                RouterMeta(
                    path = router.path, group = router.group,
                    type = RouterMeta.Type.ISERVICE
                ).apply {
                    this.element = element
                }
            }
            else -> {// 其他情况使用了Router注解，直接抛出异常
                MSG.print("$element type:$type not support router")
                throw RuntimeException()
            }
        }
    }

    /**
     * 利用kotlinpoet生成代码
     */
    private fun generateClassByKotlinPoet() {
        routerMetaByGroup.forEach { (group, routerMetas) ->
            // 分别生成两个类并实现上面两个接口，以便在Router中获取
            // 1.先来生成 根据path集合将RouterMeta包装类存入一个map  的类
            generateRouterPathByKotlinPoet(group, routerMetas)
        }

        fileNameByGroup.forEach { (group, clzName) ->
            // 2.再生成 根据group将1.生成的类存入一个map  的类
            generateRouterGroupByKotlinPoet(group, clzName)
        }
    }

    /**
     * 生成RouterPath类
     */
    private fun generateRouterPathByKotlinPoet(
        group: String,
        routerMetas: MutableList<RouterMeta>
    ) {
        // 可以自己先实现一个，再参考着写
//        class RouterPathTest : IRouterPath {
//            override fun cacheInRouterMetaByPath(map: LinkedHashMap<String, RouterMeta>) {
//                map["path1"] = RouterMeta(
//                    type = RouterMeta.Type.ACTIVITY,
//                    path = "path1",
//                    group = "group1",
//                    clazz = TestActivity::class.java
//                )
//                map["path2"] = RouterMeta(
//                    type = RouterMeta.Type.ACTIVITY,
//                    path = "path2",
//                    group = "group1",
//                    clazz = Test2Activity::class.java
//                )
//            }
//        }

        // 1.创建方法，方法名为 cacheInRouterMetaByPath
        val funcSpecBuilder = FunSpec.builder(Const.METHOD_IROUTER_PATH)
            .addModifiers(KModifier.OVERRIDE)// 方法标识override关键字
            .addParameter(//添加入参 map: LinkedHashMap<String, RouterMeta>
                "map",
                LinkedHashMap::class.parameterizedBy(String::class, RouterMeta::class)
            )

        // 2.为方法添加代码
//        map["path1"] = RouterMeta(
//            type = RouterMeta.Type.ACTIVITY,
//            path = "path1",
//            group = "group1",
//            clazz = TestActivity::class.java
//        )
        routerMetas.forEach { routerMeta ->
            funcSpecBuilder.addStatement(
                """
                    |map[%S] = RouterMeta(
                    |   type = %T.%L,
                    |   path = %S,
                    |   group = %S,
                    |   clazz = %T::class.java
                    |)
                    |
                """.trimMargin(),
                routerMeta.path,//key
                RouterMeta.Type::class,//type的类
                routerMeta.type,//type的值
                routerMeta.path,//path
                routerMeta.group,//group
                routerMeta.element!!.asType()//clazz
            )
        }

        // 3.创建类
        val superInter = ClassName(Const.IROUTER_PACKAGE, Const.IROUTER_PATH_SIMPLENAME)
        //生成的文件名 如：Router_module_a_path
        val fileName = String.format(Const.FILENAME_FORMAT_PATH, Const.FILENAME_PREFIX, group)
        val typeSpec = TypeSpec.classBuilder(fileName)
            .addFunction(funcSpecBuilder.build()) // 类中添加方法
            .addSuperinterface(superInter) // 实现IRouterPath接口：根据path集合将RouterMeta存入缓存
            .build()

        // 4.创建文件
        FileSpec.builder(Const.GENERATE_PACKAGE, fileName)//包名，文件名
            .addType(typeSpec)
            .build()
            .writeTo(mFiler) // 写入文件

        MSG.print("创建文件成功：${fileName}")

        // 加入缓存中，后续生成RouterGroup类用
        fileNameByGroup[group] = fileName
    }

    /**
     * 生成RouterGroup类
     */
    private fun generateRouterGroupByKotlinPoet(group: String, clzName: String) {
//        class RouterGroupTest : IRouterGroup {
//            override fun cacheInRouterPathByGroup(map: LinkedHashMap<String, Class<out IRouterPath>>) {
//                map["group1"] = RouterPathTest::class.java
//            }
//        }
        // 1.创建方法，方法名为 cacheInRouterPathByGroup
        val routePathInter = ClassName(Const.IROUTER_PACKAGE, Const.IROUTER_PATH_SIMPLENAME)
        val funcSpecBuilder = FunSpec.builder(Const.METHOD_IROUTER_GROUP)
            .addModifiers(KModifier.OVERRIDE)// 方法标识override关键字
            .addParameter(//添加入参 map: LinkedHashMap<String, Class<out IRouterPath>>
                "map",
                LinkedHashMap::class.java.asClassName().parameterizedBy(
                    String::class.asTypeName(),//String
                    Class::class.java.asClassName().parameterizedBy(//Class<out IRouterPath>
                        WildcardTypeName.producerOf(routePathInter)
                    )
                )
            )


        // 2.为方法添加代码  map["group1"] = RouterPathTest::class.java
        // 生成的RouterPath类
        val generatedRoutePath = ClassName(Const.GENERATE_PACKAGE, clzName)
        funcSpecBuilder.addStatement(
            "map[%S] = %T::class.java".trimMargin(),
            group,
            generatedRoutePath
        )

        // 3.创建类
        //生成的文件名 如：Router_module_a_group
        val routeGroupInter = ClassName(Const.IROUTER_PACKAGE, Const.IROUTER_GROUP_SIMPLENAME)
        val fileName = String.format(Const.FILENAME_FORMAT_GROUP, Const.FILENAME_PREFIX, group)
        val typeSpec = TypeSpec.classBuilder(fileName)
            .addFunction(funcSpecBuilder.build()) // 类中添加方法
            .addSuperinterface(routeGroupInter) // 实现IRouterGroup接口：根据group将IRouterPath存入缓存
            .build()

        // 4.创建文件
        FileSpec.builder(Const.GENERATE_PACKAGE, fileName)//包名，文件名
            .addType(typeSpec)
            .build()
            .writeTo(mFiler) // 写入文件

        MSG.print("创建文件成功：${fileName}")
    }
}